#include "StdAfx.h"
#include "SegmentedWorld.h"
#include "IVehicleSystem.h"
#include "IRenderAuxGeom.h"

#define MINIMUM_GRID_SIZE 3
#define MAXIMUM_GRID_SIZE 32

string CSegmentedWorld::s_levelName;
XmlNodeRef CSegmentedWorld::s_levelSurfaceTypes;
XmlNodeRef CSegmentedWorld::s_levelObjects;

CSegmentedWorld::CSegmentedWorld()
: m_pPoolAllocator(NULL),
	m_bSegmentedWorld(false),
	m_bInitialWorldReady(false)
{
	RegisterConsoleVariables();

	if(gEnv->pGame && gEnv->pGame->GetIGameFramework())
	{
		CRY_ASSERT_MESSAGE(gEnv->pGame->GetIGameFramework()->GetILevelSystem(), "Unable to register as levelsystem listener!");
		if(gEnv->pGame->GetIGameFramework()->GetILevelSystem())
		{
			gEnv->pGame->GetIGameFramework()->GetILevelSystem()->AddListener(this);
		}
	}
}

CSegmentedWorld::~CSegmentedWorld()
{
	SAFE_DELETE(m_pPoolAllocator);
	
	if(gEnv->pGame && gEnv->pGame->GetIGameFramework())
	{
		if(gEnv->pGame->GetIGameFramework()->GetILevelSystem())
			gEnv->pGame->GetIGameFramework()->GetILevelSystem()->RemoveListener( this );
	}
}

void CSegmentedWorld::RegisterConsoleVariables()
{
	REGISTER_CVAR(sw_gridSize, 6, 0, "Number of active grids in both column and line for segmented world");
	REGISTER_CVAR(sw_debugInfo, 1, 0, "Segmented World Debug Info (0=disable, 1=grid, 2=position, 3=memory)");
}

void CSegmentedWorld::OnLoadingStart(ILevelInfo *pLevel)
{
	if(!pLevel)
		return;

	Reset();

	string levelPath = string(pLevel->GetPath()) + "/";
	XmlNodeRef levelInfoNode = GetISystem()->LoadXmlFromFile(levelPath + "levelinfo.xml");
	int iSegmentedWorld = 0;
	if (levelInfoNode)
		levelInfoNode->getAttr("SegmentedWorld", iSegmentedWorld);

	m_bSegmentedWorld = iSegmentedWorld != 0;
	if (m_bSegmentedWorld)
	{
		levelInfoNode->getAttr("SegmentedWorldSizeMeters", m_segmentSizeMeters);
		levelInfoNode->getAttr("SegmentedWorldMinX", m_segmentsMin.x);
		levelInfoNode->getAttr("SegmentedWorldMinY", m_segmentsMin.y);
		levelInfoNode->getAttr("SegmentedWorldMaxX", m_segmentsMax.x);
		levelInfoNode->getAttr("SegmentedWorldMaxY", m_segmentsMax.y);

		if(m_segmentsMin.x > m_segmentsMax.x || m_segmentsMin.y > m_segmentsMax.y)
		{
			CRY_ASSERT(false);
			GameWarning("Invalid segmented world bounds info");
			gEnv->p3DEngine->SetSegmentsManager(0);
			return;
		}

		s_levelName = pLevel->GetName();
		s_levelName.MakeLower();

		XmlNodeRef xmlLevelData = gEnv->pSystem->LoadXmlFromFile(levelPath + "leveldata.xml");
		s_levelSurfaceTypes = xmlLevelData->findChild("SurfaceTypes");

		XmlNodeRef xmlMission = gEnv->pSystem->LoadXmlFromFile(levelPath + pLevel->GetDefaultGameType()->xmlFile);
		s_levelObjects = xmlMission->findChild("Objects");

		gEnv->p3DEngine->SetSegmentsManager(this);

		m_invSegmentSizeMeters = 1.0f / m_segmentSizeMeters;
		m_neededSegmentsMin.x = 0;
		m_neededSegmentsMin.y = 0;
		m_neededSegmentsMax.x = sw_gridSize;
		m_neededSegmentsMax.y = sw_gridSize;

		assert(m_pPoolAllocator->GetTotalMemory().nAlloc == 0);
	}
	else
	{
		gEnv->p3DEngine->SetSegmentsManager(0);
	}
}

void CSegmentedWorld::OnLoadingComplete(ILevel *pLevel)
{
	if(!pLevel || !s_levelObjects)
		return;

	// set all the global entities invisible
	EntityId id;
	for(int i = 0; i < s_levelObjects->getChildCount(); i++)
	{
		XmlNodeRef xmlEntity = s_levelObjects->getChild(i);
		if(xmlEntity->getAttr("EntityId", id))
		{
			IEntity *pEntity = gEnv->pEntitySystem->GetEntity(id);
			if(pEntity)
			{
				pEntity->Invisible(true);
			}
		}
	}
}

void CSegmentedWorld::OnUnloadComplete(ILevel* pLevel)
{
	if(!pLevel)
		return;

	gEnv->p3DEngine->SetSegmentsManager(0);
}

void CSegmentedWorld::Reset()
{
	sw_gridSize = CLAMP(sw_gridSize, MINIMUM_GRID_SIZE, MAXIMUM_GRID_SIZE);

	m_arrSegments.clear();

	m_bInitialWorldReady = false;
	m_bSegmentedWorld = false;
	m_accSegmentOffset = Vec2i(ZERO);
	m_segmentsMin = Vec2i(ZERO);
	m_segmentsMax = Vec2i(ZERO);

	SAFE_DELETE(m_pPoolAllocator);
	int poolSize = sw_gridSize * sw_gridSize * 2;
	m_pPoolAllocator = new stl::TPoolAllocator<CSegmentNode>(poolSize);

	s_levelSurfaceTypes = NULL;
	s_levelObjects = NULL;
}

CSegmentNode *CSegmentedWorld::PreloadSegment(int wx, int wy)
{
	CSegmentNode *pSeg = NULL;

	if(pSeg = FindSegmentByWorldCoords(wx, wy))
		return pSeg;

	assert(m_pPoolAllocator);
	void *pAlloc = m_pPoolAllocator->Allocate();

	pSeg = new(pAlloc) CSegmentNode(this, wx, wy, wx - m_neededSegmentsMin.x, wy - m_neededSegmentsMin.y);

	assert(pSeg);
	m_arrSegments.push_back(pSeg);

	return pSeg;
}

Vec2i CalcSWMove(int lx, int ly, const Vec2i &vMin, const Vec2i &vMax, int iThreshold)
{
	Vec2i vCenter((vMax.x - vMin.x) / 2, (vMax.y - vMin.y) / 2);
	Vec2i vThreshold(iThreshold, iThreshold);
	Vec2i vMinOK = vCenter - vThreshold;
	Vec2i vMaxOK = vCenter + vThreshold;

	Vec2i vOfs;

	// handle odd sized rectangles as well
	bool xOdd = (vMax.x - vMin.x) % 2 == 1;
	bool yOdd = (vMax.y - vMin.y) % 2 == 1;

	if (xOdd && lx <= vMinOK.x)
		vOfs.x = lx - vMinOK.x - 1; // negative, move left
	else if (!xOdd && lx < vMinOK.x)
		vOfs.x = lx - vMinOK.x; // negative, move left
	else if (lx >= vMaxOK.x)
		vOfs.x = lx - vMaxOK.x + 1; // positive, move right
	else
		vOfs.x = 0;

	if (yOdd && ly <= vMinOK.y)
		vOfs.y = ly - vMinOK.y - 1; // negative, move left
	else if (!yOdd && ly < vMinOK.y)
		vOfs.y = ly - vMinOK.y; // negative, move up
	else if (ly >= vMaxOK.y)
		vOfs.y = ly - vMaxOK.y + 1; // positive, move down
	else
		vOfs.y = 0;

	return vOfs;
}

bool CSegmentedWorld::LoadInitialWorld(const Vec3 &pos)
{
	if(m_bInitialWorldReady)
		return true;

	Vec2i neededSegmentsMin(m_neededSegmentsMin);
	Vec2i neededSegmentsMax(m_neededSegmentsMax);
	Vec2i posSS = WorldToGridPosition(pos);
	Vec2i vOfs = CalcSWMove(posSS.x, posSS.y, neededSegmentsMin, neededSegmentsMax, 1);

	neededSegmentsMin += vOfs;
	neededSegmentsMax += vOfs;

	gEnv->p3DEngine->SetSegmentedWorldActive(true);
	for (int wx = neededSegmentsMin.x; wx < neededSegmentsMax.x; ++wx)
	{
		for (int wy = neededSegmentsMin.y; wy < neededSegmentsMax.y; ++wy)
		{
			CSegmentNode *pSeg = PreloadSegment(wx, wy);
			pSeg->ForceLoad();
		}
	}
	// CS - update connections..
	gEnv->p3DEngine->GetIVisAreaManager()->UpdateConnections();
	gEnv->p3DEngine->SetSegmentedWorldActive(false);

	m_bInitialWorldReady = true;

	return true;
}

void CSegmentedWorld::DeleteSegment(int wx, int wy)
{
	for (int i = 0; i < m_arrSegments.size(); ++i)
	{
		CSegmentNode *pSeg = m_arrSegments[i];
		if (pSeg->m_wx == wx && pSeg->m_wy == wy)
		{
			m_arrSegments.erase(m_arrSegments.begin() + i);

			pSeg->~CSegmentNode();
			m_pPoolAllocator->Deallocate(pSeg);
			return;
		}
	}
}

bool CSegmentedWorld::NeedToUpdateSegment(int wx, int wy)
{
	CSegmentNode* pSeg = FindSegmentByWorldCoords(wx, wy);
	if (!pSeg) return true;

	return !pSeg->StreamingFinished();
}

Vec2i CSegmentedWorld::SelectSegmentToUpdate(const Vec2i &focalPointWC)
{
	// Select the segment closest to the player to update
	Vec2i segmentToUpdateWC = Vec2i(0, 0);
	int segmentToUpdateDistance = INT_MAX;
	for (int x = m_neededSegmentsMin.x; x < m_neededSegmentsMax.x; ++x)
	{
		for (int y = m_neededSegmentsMin.y; y < m_neededSegmentsMax.y; ++y)
		{
			if (!NeedToUpdateSegment(x, y)) continue;
			Vec2i wc = Vec2i(x, y);
			int distance = (focalPointWC - wc).GetLength2();
			if (distance < segmentToUpdateDistance)
			{
				segmentToUpdateWC = wc;
				segmentToUpdateDistance = distance;
			}
		}
		// Also consider all segments outside the boundaries
		for (int i = 0; i < m_arrSegments.size(); ++i)
		{
			Vec2i wc = Vec2i(m_arrSegments[i]->m_wx, m_arrSegments[i]->m_wy);
			if (wc.x < m_neededSegmentsMin.x || wc.x >= m_neededSegmentsMax.x ||
				wc.y < m_neededSegmentsMin.y || wc.y >= m_neededSegmentsMax.y)
			{
				int distance = (focalPointWC - wc).GetLength2();
				if (distance < segmentToUpdateDistance)
				{
					segmentToUpdateWC = wc;
					segmentToUpdateDistance = distance;
				}
			}
		}
	}
	return segmentToUpdateWC;
}

void CSegmentedWorld::Update()
{
	if (gEnv->p3DEngine->GetSegmentsManager() != this)
		return;

	gEnv->p3DEngine->GetIVisAreaManager()->ReleaseInactiveSegments();
	gEnv->p3DEngine->GetITerrain()->ReleaseInactiveSegments();

	IActor *pClientPlayer = gEnv->pGame->GetIGameFramework()->GetClientActor();
	if(!pClientPlayer)
		return;

	// set player position as our world focal point
	m_worldFocalPoint = pClientPlayer->GetEntity()->GetWorldPos();

//#ifdef _DEBUG
//	// assert if player has been teleported with a long distance
//	static Vec3 s_lastWorldFocalPoint = m_worldFocalPoint;
//	float posChange = (m_worldFocalPoint - s_lastWorldFocalPoint).len2();
//	assert(posChange < m_segmentSizeMeters * m_segmentSizeMeters);
//	s_lastWorldFocalPoint = m_worldFocalPoint;
//#endif

	if(!m_bInitialWorldReady)
	{
		// load initial segmented world according to player's position
		LoadInitialWorld(m_worldFocalPoint);
		return;
	}

	// player position in grid space
#if USE_RELATIVE_COORD
	m_gridFocalPointLC = WorldToGridPosition(m_worldFocalPoint);
	m_gridFocalPointWC = m_neededSegmentsMin + m_gridFocalPointLC;
#else
	m_gridFocalPointWC = WorldToGridPosition(m_worldFocalPoint);
	m_gridFocalPointLC = m_gridFocalPointWC - m_accSegmentOffset;
#endif
	
	int iThreshold = 1;//gEnv->pConsole->GetCVar("sw_autoMove")->GetIVal();
	if (iThreshold > 0)
	{
		Vec2i vOfs = CalcSWMove(m_gridFocalPointLC.x, m_gridFocalPointLC.y, m_neededSegmentsMin, m_neededSegmentsMax, iThreshold);

		if (vOfs.x < -1) vOfs.x = -1;
		if (vOfs.x > +1) vOfs.x = +1;
		if (vOfs.y < -1) vOfs.y = -1;
		if (vOfs.y > +1) vOfs.y = +1;

		m_neededSegmentsMin += vOfs;
		m_neededSegmentsMax += vOfs;
		m_accSegmentOffset += vOfs;

#if USE_RELATIVE_COORD
		// offset position for existing objects
		Vec3 offset((float)(-vOfs.y * m_segmentSizeMeters), (float)(-vOfs.x * m_segmentSizeMeters), 0);
		if(vOfs.x != 0 || vOfs.y != 0)
		{
			for(int i = 0; i < m_arrSegments.size(); i++)
			{
				CSegmentNode *pSeg = m_arrSegments[i];
				pSeg->SetLocalCoords(pSeg->m_wx - m_neededSegmentsMin.x, pSeg->m_wy - m_neededSegmentsMin.y);
			}

			gEnv->p3DEngine->GetIVisAreaManager()->OffsetPosition(offset);

			IEntityItPtr pIt = gEnv->pEntitySystem->GetEntityIterator();
			while (IEntity* pEnt = pIt->Next())
			{
				if (pEnt->GetParent() == 0)
					pEnt->SetPos(pEnt->GetPos() + offset, 0, false);

				if(IVehicle *pVehicle = gEnv->pGameFramework->GetIVehicleSystem()->GetVehicle(pEnt->GetId()))
					pVehicle->OffsetPosition(offset);
			}

			gEnv->p3DEngine->OffsetPosition(offset);

			// Update focal points
			m_worldFocalPoint = pClientPlayer->GetEntity()->GetWorldPos();
			m_gridFocalPointLC = WorldToGridPosition(m_worldFocalPoint);
			m_gridFocalPointWC = m_neededSegmentsMin + m_gridFocalPointLC;

			GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_SHIFT_WORLD, (UINT_PTR)&offset, NULL);
		}
		gEnv->pRenderer->OffsetPosition(offset);
#endif
	}

	gEnv->p3DEngine->SetSegmentedWorldActive(true);
	Vec2i wc = SelectSegmentToUpdate(m_gridFocalPointWC);
	UpdateSegment(wc.x, wc.y);
	gEnv->p3DEngine->SetSegmentedWorldActive(false);

	// display debug info on screen
	DrawDebugInfo();
}

void CSegmentedWorld::UpdateSegment(int wx, int wy)
{
	CSegmentNode* pSeg = FindSegmentByWorldCoords(wx, wy);

	// check if segment is out of active boundaries
	if (wx < m_neededSegmentsMin.x || wx >= m_neededSegmentsMax.x ||
		wy < m_neededSegmentsMin.y || wy >= m_neededSegmentsMax.y)
	{
		if(!pSeg)
			return;

		DeleteSegment(wx, wy);
		pSeg->SetStatus(ESEG_IDLE);
	}
	else
	{
		if(!pSeg)
		{
			// allocate segment if not exit
			pSeg = PreloadSegment(wx, wy);
			pSeg->SetStatus(ESEG_INIT);
		}
		else
		{
			// request to start streaming
			pSeg->SetStatus(ESEG_STREAM);
		}
	}
	assert(pSeg);
	pSeg->Update();
}

bool CSegmentedWorld::FindSegment(ITerrain* pTerrain, Vec3 pt, int &nSID)
{
#if USE_RELATIVE_COORD
	Vec2i ptSS = WorldToGridPosition(pt);
	CSegmentNode* pSeg = FindSegmentByLocalCoords(ptSS.x, ptSS.y);
	if(pSeg && pSeg->OffsetApplied(pSeg->m_wx - m_neededSegmentsMin.x, pSeg->m_wy - m_neededSegmentsMin.y))
	{
		nSID = pSeg->m_nSID;
	}
	else
	{
		nSID = -1;
	}
#else
	Vec2i ptSS = WorldToGridPosition(pt);
	CSegmentNode* pSeg = FindSegmentByWorldCoords(ptSS.x, ptSS.y);
	if(pSeg)
	{
		nSID = pSeg->m_nSID;
	}
	else
	{
		nSID = -1;
	}
#endif
	return true;
}

bool CSegmentedWorld::IsInsideSegmentedWorld(const Vec3& pt)
{
	return pt.x >= 0 && pt.y >= 0 &&
		pt.y < (m_neededSegmentsMax.x - m_neededSegmentsMin.x) * m_segmentSizeMeters &&
		pt.x < (m_neededSegmentsMax.y - m_neededSegmentsMin.y) * m_segmentSizeMeters;
}

void CSegmentedWorld::GetTerrainSizeInMeters(int &x, int &y)
{
	x = (m_neededSegmentsMax.x - m_neededSegmentsMin.x) * (int)m_segmentSizeMeters;
	y = (m_neededSegmentsMax.y - m_neededSegmentsMin.y) * (int)m_segmentSizeMeters;
}

CSegmentNode *CSegmentedWorld::FindSegmentByID(int nSID)
{
	for (int i = 0; i < m_arrSegments.size(); ++i)
		if (m_arrSegments[i]->m_nSID == nSID)
			return m_arrSegments[i];
	return 0;
}

CSegmentNode *CSegmentedWorld::FindSegmentByWorldCoords(int wx, int wy)
{
	for (int i = 0; i < m_arrSegments.size(); ++i)
	{
		CSegmentNode *pSeg = m_arrSegments[i];
		if (pSeg->m_wx == wx && pSeg->m_wy == wy)
			return pSeg;
	}
	return 0;
}

CSegmentNode *CSegmentedWorld::FindSegmentByLocalCoords(int lx, int ly)
{
	for (int i = 0; i < m_arrSegments.size(); ++i)
	{
		CSegmentNode *pSeg = m_arrSegments[i];
		if (pSeg->m_lx == lx && pSeg->m_ly == ly)
			return pSeg;
	}
	return 0;
}

void CSegmentedWorld::DrawDebugInfo()
{
	if (!sw_debugInfo || gEnv->IsDedicated())
		return;

	int width  = gEnv->pRenderer->GetWidth();
	int height = gEnv->pRenderer->GetHeight();
	GetISystem()->GetIRenderer()->Set2DMode(true, width, height);

	static float debugGridSize = 16;
	if(m_segmentsMax.x > 32)
		debugGridSize = 8;
	static Vec2 debugGridOrigin(50, 100);

	DrawSegmentGrid(debugGridOrigin, debugGridSize);
	if(sw_debugInfo == 1)
		PrintSegmentInfo(debugGridOrigin, debugGridSize);
	else if(sw_debugInfo == 2)
		PrintMemoryInfo(debugGridOrigin, debugGridSize);

	GetISystem()->GetIRenderer()->Set2DMode(false, width, height);
}

void CSegmentedWorld::DrawSegmentGrid(const Vec2 &origin, float size)
{
	IRenderAuxGeom* pAux = GetISystem()->GetIRenderer()->GetIRenderAuxGeom();

	for (int x = m_segmentsMin.x; x <= m_segmentsMax.x; ++x)
	{
		for (int y = m_segmentsMin.y; y <= m_segmentsMax.y; ++y)
		{
			CSegmentNode *pSeg = FindSegmentByWorldCoords(x, y);
			ColorB color =
				pSeg == 0 ? 0xff7f7f7f :
				!pSeg->StreamingSucceeded() ? 0xff00ffff :
				0xff00ff00;
			float sx = origin.x + (float)(x - m_segmentsMin.x) * size;
			float sy = origin.y + (float)(y - m_segmentsMin.y) * size;
			pAux->DrawLine(Vec3(sx, sy, 0), color, Vec3(sx + size - 1, sy, 0), color);
			pAux->DrawLine(Vec3(sx + size - 1, sy, 0), color, Vec3(sx + size - 1, sy + size - 1, 0), color);
			pAux->DrawLine(Vec3(sx + size - 1, sy + size - 1, 0), color, Vec3(sx, sy + size - 1, 0), color);
			pAux->DrawLine(Vec3(sx, sy + size - 1, 0), color, Vec3(sx, sy, 0), color);
		}
	}

	Vec3 playerGridPos = WorldToScreenPosition(m_worldFocalPoint);
	playerGridPos.x -= m_segmentsMin.x;
	playerGridPos.y -= m_segmentsMin.y;
#if USE_RELATIVE_COORD
	playerGridPos.x += m_neededSegmentsMin.x;
	playerGridPos.y += m_neededSegmentsMin.y;
#endif
	playerGridPos *= size;
	playerGridPos += Vec3(origin.x, origin.y, 0);
	pAux->DrawLine(playerGridPos - Vec3(2, 0, 0), 0xff0000ff, playerGridPos + Vec3(2, 0, 0), 0xff0000ff, 5);

	const CCamera& camera = gEnv->pRenderer->GetCamera();
	{
		Vec3 lookDir = camera.GetViewdir();
		std::swap(lookDir.x, lookDir.y);
		lookDir.z = 0;
		lookDir.Normalize();

		pAux->DrawLine(playerGridPos, 0xff0000ff, playerGridPos + 10 * lookDir, 0xff0000ff, 2);
	}
}

void CSegmentedWorld::PrintSegmentInfo(const Vec2 &origin, float size)
{
	SDrawTextInfo ti;
	ti.flags = eDrawText_FixedSize | eDrawText_2D | eDrawText_Monospace;

	Vec3 step(0, 16, 0);
	Vec3 pos(origin.x, origin.y, 1);
	pos.x += m_segmentsMax.x * size + 50;

	Vec3 worldPosLC, worldPosWC;
#if USE_RELATIVE_COORD
	ti.xscale = ti.yscale = 1.5f;
	gEnv->pRenderer->DrawTextQueued(pos, ti, string("Use Relative Coordinates"));
	pos += step;
	ti.xscale = ti.yscale = 1.2f;
	worldPosLC = m_worldFocalPoint;
	worldPosWC = worldPosLC + GridToWorldPosition(m_neededSegmentsMin);
#else
	ti.xscale = ti.yscale = 1.5f;
	gEnv->pRenderer->DrawTextQueued(pos, ti, string("Use Absolute Coordinates"));
	pos += step;
	ti.xscale = ti.yscale = 1.2f;
	worldPosWC = m_worldFocalPoint;
	worldPosLC = worldPosWC - GridToWorldPosition(m_neededSegmentsMin);
#endif
	pos += step;
	gEnv->pRenderer->DrawTextQueued(pos, ti, string().Format("Player Global World Position: %.2f, %.2f, %.2f", worldPosWC.x, worldPosWC.y, worldPosWC.z).c_str());
	pos += step;
	gEnv->pRenderer->DrawTextQueued(pos, ti, string().Format("Player Global Grid Position: %d, %d", m_gridFocalPointWC.x, m_gridFocalPointWC.y).c_str());
	pos += step;
	gEnv->pRenderer->DrawTextQueued(pos, ti, string().Format("Global Segments: (%d, %d), (%d,%d)", m_segmentsMin.x, m_segmentsMin.y, m_segmentsMax.x, m_segmentsMax.y).c_str());
#ifdef _DEBUG
	pos += step;
	pos += step;
	gEnv->pRenderer->DrawTextQueued(pos, ti, string().Format("Player Local World Position: %.2f, %.2f, %.2f", worldPosLC.x, worldPosLC.y, worldPosLC.z).c_str());
	pos += step;
	gEnv->pRenderer->DrawTextQueued(pos, ti, string().Format("Player Local Grid Position: %d, %d", m_gridFocalPointLC.x, m_gridFocalPointLC.y).c_str());
	pos += step;
	gEnv->pRenderer->DrawTextQueued(pos, ti, string().Format("Local Segments: (%d, %d), (%d,%d)", m_neededSegmentsMin.x, m_neededSegmentsMin.y, m_neededSegmentsMax.x, m_neededSegmentsMax.y).c_str());
#endif
}

void CSegmentedWorld::PrintMemoryInfo(const Vec2 &origin, float size)
{
	SDrawTextInfo ti;
	ti.flags = eDrawText_FixedSize | eDrawText_2D | eDrawText_Monospace;
	ti.xscale = ti.yscale = 1.2f;

	Vec3 step(0, 16, 0);
	Vec3 pos(origin.x, origin.y, 1);
	pos.x += m_segmentsMax.x * size + 50;

	const stl::SPoolMemoryUsage memUsage = m_pPoolAllocator->GetTotalMemory();
	gEnv->pRenderer->DrawTextQueued(pos, ti, string().Format("Segment Heap: %d KB Used, %d KB Freed, %d KB Unused",
		(int)(memUsage.nUsed>>10), (int)(memUsage.nPoolFree()>>10), (int)(memUsage.nNonPoolFree()>>10)).c_str());
}